<#@ template language="VB" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.VB.ttinclude"#><#@ output extension=".vb"#><# ' Copyright (c) Microsoft Corporation. All rights reserved. Dim code As New CodeGenerationTools(Me) Dim loader As New MetadataLoader(Me) Dim region As New CodeRegion(Me) Dim ef As New MetadataTools(Me) Dim inputFile As String = "$edmxInputFile$" Dim metadataWorkspace As MetadataWorkspace = Nothing Dim allMetadataLoaded As Boolean = loader.TryLoadAllMetadata(inputFile, metadataWorkspace) Dim ItemCollection As EdmItemCollection = DirectCast(metadataWorkspace.GetItemCollection(DataSpace.CSpace), EdmItemCollection) Dim originalValueMembers As New OriginalValueMembers(allMetadataLoaded, metadataWorkspace, ef) Dim namespaceName As String = code.VsNamespaceSuggestion() Dim fileManager As EntityFrameworkTemplateFileManager = EntityFrameworkTemplateFileManager.Create(Me) ' Write out support code to primary template output file WriteHeader(fileManager) BeginNamespace(namespaceName, code) WriteObjectChangeTracker() WriteIObjectWithChangeTracker() WriteCustomObservableCollection() WriteINotifyComplexPropertyChanging() WriteEqualityComparer() EndNamespace(namespaceName) ' Emit Entity Types For Each loopEntity As EntityType In ItemCollection.GetItems(Of EntityType)().OrderBy(Function(e) e.Name) Dim entity As EntityType = loopEntity fileManager.StartNewFile(entity.Name & ".vb") BeginNamespace(namespaceName, code) WriteEntityTypeSerializationInfo(entity, ItemCollection, code, ef) #> Partial <#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.MustInheritOption(entity))#>Class <#=code.Escape(entity)#><#=code.StringBefore(Environment.NewLine & CodeRegion.GetIndent(region.CurrentIndentLevel + 2) & "Inherits ", code.Escape(entity.BaseType))#> Implements IObjectWithChangeTracker Implements INotifyPropertyChanged <# region.Begin("Primitive Properties") For Each edmProperty As EdmProperty In entity.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is PrimitiveType AndAlso p.DeclaringType Is entity) #> <#=Accessibility.ForProperty(edmProperty)#> Property <#=code.Escape(edmProperty)#>() As <#=code.Escape(edmProperty.TypeUsage)#> Get Return <#=code.FieldName(edmProperty)#> End Get Set(ByVal value As <#=code.Escape(edmProperty.TypeUsage)#>) <# If DirectCast(edmProperty.TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary Then Dim edmPropertyTemp As EdmProperty = edmProperty If ef.IsKey(edmPropertyTemp) OrElse entity.NavigationProperties.Where(Function(np) np.GetDependentProperties().Contains(edmPropertyTemp)).Any() Then #> If Not EqualityComparer.BinaryEquals(<#=code.FieldName(edmProperty)#>, value) Then <# Else #> If <#=code.FieldName(edmProperty)#> IsNot value Then <# End If Else #> If Not Equals(<#=code.FieldName(edmProperty)#>, value) Then <# End If If ef.IsKey(edmProperty) Then Dim errorMessage As String = String.Format( "The property '{0}' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.", edmProperty.Name) #> If ChangeTracker.ChangeTrackingEnabled AndAlso ChangeTracker.State <> ObjectState.Added Then Throw New InvalidOperationException("<#=errorMessage#>") End If <# ElseIf originalValueMembers.IsOriginalValueMember(edmProperty) Then #> ChangeTracker.RecordOriginalValue("<#=edmProperty.Name#>", <#=code.FieldName(edmProperty)#>) <# End If Dim ep As EdmProperty = edmProperty Dim hasDependentProperties As Boolean = entity.NavigationProperties.Where(Function(p) p.GetDependentProperties().Contains(ep)).Any() If hasDependentProperties Then #> If Not IsDeserializing Then <# End If For Each np As NavigationProperty In entity.NavigationProperties.Where(Function(p) p.GetDependentProperties().Contains(ep)) Dim principalProperty As EdmProperty = ef.GetCorrespondingPrincipalProperty(np, edmProperty) Dim equality As String = If(DirectCast(principalProperty.TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary, "EqualityComparer.Binary", String.Empty) #> If <#=code.Escape(np)#> IsNot Nothing AndAlso Not <#=equality#>Equals(<#=code.Escape(np)#>.<#=code.Escape(principalProperty)#>, value) Then <# If Not (np.GetDependentProperties().Where(Function(p) ef.IsNullable(p)).Any() AndAlso np.GetDependentProperties().Count() > 1) Then #> <#=code.Escape(np)#> = Nothing <# Else #> Dim previousValue As <#=code.Escape(np.TypeUsage)#> = <#=code.FieldName(np)#> <#=code.FieldName(np)#> = Nothing Fixup<#=np.Name#>(previousValue, skipKeys:=True) OnNavigationPropertyChanged("<#=np.Name#>") <# End If #> End If <# Next If hasDependentProperties Then #> End If <# End If #> <#=code.FieldName(edmProperty)#> = value OnPropertyChanged("<#=edmProperty.Name#>") End If End Set End Property Private <#=code.FieldName(edmProperty)#> As <#=code.Escape(edmProperty.TypeUsage)#><#=code.StringBefore(" = ", code.CreateLiteral(edmProperty.DefaultValue))#> <# Next region.End() region.Begin("Complex Properties") For Each edmProperty As EdmProperty In entity.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is ComplexType AndAlso p.DeclaringType Is entity) #> <#=Accessibility.ForProperty(edmProperty)#> Property <#=code.Escape(edmProperty)#>() As <#=code.Escape(edmProperty.TypeUsage)#> <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>Get If Not <#=InitializedTrackingField(edmProperty, code)#> AndAlso <#=code.FieldName(edmProperty)#> Is Nothing Then <#=code.FieldName(edmProperty)#> = New <#=code.Escape(edmProperty.TypeUsage)#>() AddHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf Handle<#=edmProperty.Name#>Changing End If <#=InitializedTrackingField(edmProperty, code)#> = True Return <#=code.FieldName(edmProperty)#> End Get Set(ByVal value As <#=code.Escape(edmProperty.TypeUsage)#>) <#=InitializedTrackingField(edmProperty, code)#> = True If Not Equals(<#=code.FieldName(edmProperty)#>, value) Then If <#=code.FieldName(edmProperty)#> IsNot Nothing Then RemoveHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf Handle<#=edmProperty.Name#>Changing End If Handle<#=edmProperty.Name#>Changing(Me, Nothing) <#=code.FieldName(edmProperty)#> = value OnPropertyChanged("<#=edmProperty.Name#>") If value IsNot Nothing Then AddHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf Handle<#=edmProperty.Name#>Changing End If End If End Set End Property Private <#=code.FieldName(edmProperty)#> As <#=code.Escape(edmProperty.TypeUsage)#> Private <#=InitializedTrackingField(edmProperty, code)#> As Boolean <# Next region.End() region.Begin("Navigation Properties") For Each navProperty As NavigationProperty In entity.NavigationProperties.Where(Function(np) np.DeclaringType Is entity) Dim inverse As NavigationProperty = ef.Inverse(navProperty) Dim fromType As EntityType = navProperty.FromEndMember.GetEntityType() Dim toType As EntityType = navProperty.ToEndMember.GetEntityType() If inverse IsNot Nothing AndAlso Not IsReadWriteAccessibleProperty(inverse) Then inverse = Nothing End If #> <# If navProperty.ToEndMember.RelationshipMultiplicity = RelationshipMultiplicity.Many Then #> <#=Accessibility.ForReadOnlyProperty(navProperty)#> Property <#=code.Escape(navProperty)#>() As TrackableCollection(Of <#=code.Escape(toType)#>) Get If <#=code.FieldName(navProperty)#> Is Nothing Then <#=code.FieldName(navProperty)#> = New TrackableCollection(Of <#=code.Escape(toType)#>) AddHandler <#=code.FieldName(navProperty)#>.CollectionChanged, AddressOf Fixup<#=navProperty.Name#> End If Return <#=code.FieldName(navProperty)#> End Get Set(ByVal value As TrackableCollection(Of <#=code.Escape(toType)#>)) If Not Object.ReferenceEquals(<#=code.FieldName(navProperty)#>, value) Then If ChangeTracker.ChangeTrackingEnabled Then Throw New InvalidOperationException("Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled") End If If <#=code.FieldName(navProperty)#> IsNot Nothing Then RemoveHandler <#=code.FieldName(navProperty)#>.CollectionChanged, AddressOf Fixup<#=navProperty.Name#> <# If ef.IsCascadeDeletePrincipal(navProperty) Then #> ' This is the principal end in an association that performs cascade deletes. ' Remove the cascade delete event handler for any entities in the current collection. For Each item As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> In <#=code.FieldName(navProperty)#> RemoveHandler ChangeTracker.ObjectStateChanging, AddressOf item.HandleCascadeDelete Next <# End If #> End If <#=code.FieldName(navProperty)#> = value If <#=code.FieldName(navProperty)#> IsNot Nothing Then AddHandler <#=code.FieldName(navProperty)#>.CollectionChanged, AddressOf Fixup<#=navProperty.Name#> <# If ef.IsCascadeDeletePrincipal(navProperty) Then #> ' This is the principal end in an association that performs cascade deletes. ' Add the cascade delete event handler for any entities that are already in the new collection. For Each item As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> In <#=code.FieldName(navProperty)#> AddHandler ChangeTracker.ObjectStateChanging, AddressOf item.HandleCascadeDelete Next <# End If #> End If OnNavigationPropertyChanged("<#=navProperty.Name#>") End If End Set End Property Private <#=code.FieldName(navProperty)#> As TrackableCollection(Of <#=code.Escape(toType)#>) <# Else #> <#=Accessibility.ForProperty(navProperty)#> Property <#=code.Escape(navProperty)#>() As <#=code.Escape(toType)#> <#=code.SpaceAfter(Accessibility.ForGetter(navProperty))#>Get Return <#=code.FieldName(navProperty)#> End Get <#=code.SpaceAfter(Accessibility.ForSetter(navProperty))#>Set(ByVal value As <#=code.Escape(toType)#>) If <#=code.FieldName(navProperty)#> IsNot value Then <# ' If this is the dependent end of an identifying relationship, the principal end can only be changed if the dependent is in the Added state and the principal's key matches the foreign key on the dependent If ef.IsPrincipalEndOfIdentifyingRelationship(DirectCast(navProperty.ToEndMember, AssociationEndMember)) Then #> If ChangeTracker.ChangeTrackingEnabled AndAlso ChangeTracker.State <> ObjectState.Added AndAlso value IsNot Nothing Then <# Dim dependents As List(Of EdmProperty) = navProperty.GetDependentProperties().ToList() Dim dependentCount As Integer = dependents.Count Dim keyMatchCondition As New StringBuilder() For i As Integer = 0 To dependentCount - 1 Dim dependentProperty As EdmProperty = dependents(i) Dim principalProperty As EdmProperty = ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty) Dim escapedDependent As String = code.Escape(dependentProperty) Dim escapedPrincipal As String = code.Escape(principalProperty) If i > 0 Then keyMatchCondition.AppendFormat(" OrElse ") End If Dim equality As String = Nothing If DirectCast(principalProperty.TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary Then equality = "Not EqualityComparer.BinaryEquals({0}, value.{1})" Else equality = "Not Equals({0}, value.{1})" End If keyMatchCondition.AppendFormat(CultureInfo.InvariantCulture, equality, escapedDependent, escapedPrincipal) Next #> ' This the dependent end of an identifying relationship, so the principal end cannot be changed if it is already set, ' otherwise it can only be set to an entity with a primary key that is the same value as the dependent's foreign key. If <#=keyMatchCondition.ToString()#> Then Throw New InvalidOperationException("The principal end of an identifying relationship can only be changed when the dependent end is in the Added state.") End If End If <# End If #> Dim previousValue As <#=code.Escape(toType)#> = <#=code.FieldName(navProperty)#> <#=code.FieldName(navProperty)#> = value Fixup<#=navProperty.Name#>(previousValue) OnNavigationPropertyChanged("<#=navProperty.Name#>") End If End Set End Property Private <#=code.FieldName(navProperty)#> As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> <# End If Next region.End() region.Begin("ChangeTracking") If entity.BaseType Is Nothing Then #> Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) If ChangeTracker.State <> ObjectState.Added AndAlso ChangeTracker.State <> ObjectState.Deleted Then ChangeTracker.State = ObjectState.Modified End If RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Protected Overridable Sub OnNavigationPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Private Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private _changeTracker As ObjectChangeTracker Public Property ChangeTracker() As ObjectChangeTracker Implements IObjectWithChangeTracker.ChangeTracker Get If _changeTracker Is Nothing Then _changeTracker = New ObjectChangeTracker() AddHandler _changeTracker.ObjectStateChanging, AddressOf HandleObjectStateChanging End If Return _changeTracker End Get Set(ByVal value As ObjectChangeTracker) If _changeTracker IsNot Nothing Then RemoveHandler _changeTracker.ObjectStateChanging, AddressOf HandleObjectStateChanging End If _changeTracker = value If _changeTracker IsNot Nothing Then AddHandler _changeTracker.ObjectStateChanging, AddressOf HandleObjectStateChanging End If End Set End Property Private Sub HandleObjectStateChanging(ByVal sender As Object, ByVal e As ObjectStateChangingEventArgs) If e.NewState = ObjectState.Deleted Then Me.ClearNavigationProperties() End If End Sub <# ' If this entity type participates in any relationships where the other end has an OnDelete ' cascade delete defined, or if it is the dependent in any identifying relationships, it needs ' an event handler to handle notifications that are fired when the parent is deleted. If ItemCollection.GetItems(Of AssociationType)().Where( Function(a) DirectCast(a.AssociationEndMembers(0).TypeUsage.EdmType, RefType).ElementType Is entity AndAlso ef.IsCascadeDeletePrincipal(a.AssociationEndMembers(1)) OrElse _ DirectCast(a.AssociationEndMembers(1).TypeUsage.EdmType, RefType).ElementType Is entity AndAlso ef.IsCascadeDeletePrincipal(a.AssociationEndMembers(0))).Any() Then #> ' This entity type is the dependent end in at least one association that performs cascade deletes. ' This event handler will process notifications that occur when the principal end is deleted. Friend Sub HandleCascadeDelete(ByVal sender As Object, ByVal e As ObjectStateChangingEventArgs) If e.NewState = ObjectState.Deleted Then Me.MarkAsDeleted() End If End Sub <# End If #> Private _isDeserializing As Boolean Protected Property IsDeserializing() As Boolean Get Return _isDeserializing End Get Private Set(ByVal value As Boolean) _isDeserializing = value End Set End Property Public Sub OnDeserializingMethod(ByVal context As StreamingContext) IsDeserializing = True End Sub Public Sub OnDeserializedMethod(ByVal context As StreamingContext) IsDeserializing = False ChangeTracker.ChangeTrackingEnabled = True End Sub <# End If For Each edmProperty As EdmProperty In entity.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is ComplexType AndAlso p.DeclaringType Is entity) #> ' <#=String.Format(CultureInfo.CurrentCulture, "Records the original values for the complex property {0}", edmProperty.Name)#> Private Sub Handle<#=edmProperty.Name#>Changing(ByVal sender As Object, ByVal args As EventArgs) If ChangeTracker.State <> ObjectState.Added AndAlso ChangeTracker.State <> ObjectState.Deleted Then ChangeTracker.State = ObjectState.Modified End If <# If originalValueMembers.IsOriginalValueMember(edmProperty) Then #> <#=code.Escape(edmProperty.TypeUsage)#>.RecordComplexOriginalValues("<#=edmProperty.Name#>", Me.<#=code.Escape(edmProperty)#>, ChangeTracker) <# End If #> End Sub <# Next Dim shadowAssociationEnds As New List(Of AssociationEndMember)() For Each association As AssociationType In ItemCollection.GetItems(Of AssociationType)().Where(Function(x As AssociationType) (Not IsForeignKeyOrIdentifyingRelationship(ef, x)) AndAlso _ ((Object.Equals(DirectCast(x.AssociationEndMembers(0).TypeUsage.EdmType, RefType).ElementType, entity) AndAlso _ x.AssociationEndMembers(0).RelationshipMultiplicity <> RelationshipMultiplicity.One AndAlso _ x.AssociationEndMembers(1).RelationshipMultiplicity <> RelationshipMultiplicity.Many) OrElse _ (Object.Equals(DirectCast(x.AssociationEndMembers(1).TypeUsage.EdmType, RefType).ElementType, entity) AndAlso _ x.AssociationEndMembers(1).RelationshipMultiplicity <> RelationshipMultiplicity.One AndAlso _ x.AssociationEndMembers(0).RelationshipMultiplicity <> RelationshipMultiplicity.Many))) Dim theAssociation As AssociationType = association If Not entity.NavigationProperties.Any(Function(x) Object.Equals(x.RelationshipType, theAssociation)) Then For i As Integer = 0 To 1 Dim targetRoleIndex As Integer = 0 If Object.Equals(DirectCast(theAssociation.AssociationEndMembers(i).TypeUsage.EdmType, RefType).ElementType, entity) Then targetRoleIndex = (i + 1) Mod 2 shadowAssociationEnds.Add(theAssociation.AssociationEndMembers(targetRoleIndex)) End If Next End If Next #> Protected <#=InheritanceOperator(entity)#>Sub ClearNavigationProperties() <# If entity.BaseType IsNot Nothing Then #> MyBase.ClearNavigationProperties() <# End If For Each navProperty As NavigationProperty In entity.NavigationProperties.Where(Function(np) np.DeclaringType Is entity) If navProperty.ToEndMember.RelationshipMultiplicity = RelationshipMultiplicity.Many Then #> <#=code.Escape(navProperty)#>.Clear() <# Else #> <#=code.Escape(navProperty)#> = Nothing <# If IsSaveReference(ef, navProperty) Then #> Fixup<#=navProperty.Name#>Keys() <# End If End If Next For Each associationEnd As AssociationEndMember In shadowAssociationEnds Dim association As AssociationType = TryCast(associationEnd.DeclaringType, AssociationType) #> <#=CreateFixupMethodName(associationEnd)#>(Nothing, True) <# Next #> End Sub <# region.End() region.Begin("Association Fixup") For Each navProperty As NavigationProperty In entity.NavigationProperties.Where(Function(np) np.DeclaringType Is entity) Dim inverse As NavigationProperty = ef.Inverse(navProperty) If inverse IsNot Nothing AndAlso Not IsReadWriteAccessibleProperty(inverse) Then inverse = Nothing End If If navProperty.ToEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.Many Then Dim skipKeysArgument As String = If(navProperty.GetDependentProperties().Where(Function(p) ef.IsNullable(p)).Any(), ", Optional ByVal skipKeys As Boolean = False", String.Empty) #> Private Sub Fixup<#=navProperty.Name#>(ByVal previousValue As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#><#= skipKeysArgument #>) <# If ef.IsCascadeDeletePrincipal(navProperty) Then #> If previousValue IsNot Nothing Then RemoveHandler ChangeTracker.ObjectStateChanging, AddressOf previousValue.HandleCascadeDelete End If If <#=code.Escape(navProperty)#> IsNot Nothing Then AddHandler ChangeTracker.ObjectStateChanging, AddressOf <#=code.Escape(navProperty)#>.HandleCascadeDelete End If <# ElseIf inverse Is Nothing AndAlso ef.IsCascadeDeletePrincipal(DirectCast(navProperty.ToEndMember, AssociationEndMember)) Then #> ' This is the dependent end in an association that performs cascade deletes. ' Update the principal's event listener to refer to the new dependent. ' This is a unidirectional relationship from the dependent to the principal, so the dependent end is ' responsible for managing the cascade delete event handler. In all other cases the principal end will manage it. If previousValue IsNot Nothing Then RemoveHandler previousValue.ChangeTracker.ObjectStateChanging, AddressOf HandleCascadeDelete End If If <#=code.Escape(navProperty)#> IsNot Nothing Then AddHandler <#=code.Escape(navProperty)#>.ChangeTracker.ObjectStateChanging, AddressOf HandleCascadeDelete End If <# End If #> If IsDeserializing Then Return End If <# If inverse IsNot Nothing Then If inverse.ToEndMember.RelationshipMultiplicity = RelationshipMultiplicity.Many Then #> If previousValue IsNot Nothing AndAlso previousValue.<#=code.Escape(inverse)#>.Contains(Me) Then previousValue.<#=code.Escape(inverse)#>.Remove(Me) End If <# Else #> If previousValue IsNot Nothing AndAlso ReferenceEquals(previousValue.<#=code.Escape(inverse)#>, Me) Then previousValue.<#=code.Escape(inverse)#> = Nothing End If <# End If If inverse.ToEndMember.RelationshipMultiplicity = RelationshipMultiplicity.Many Then #> If <#=code.Escape(navProperty)#> IsNot Nothing Then If Not <#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#>.Contains(Me) Then <#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#>.Add(Me) End If <# For Each dependentProperty As EdmProperty In navProperty.GetDependentProperties() #> <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#> <# Next If navProperty.GetDependentProperties().Where(Function(p) ef.IsNullable(p)).Any() Then #> ElseIf Not skipKeys Then <# For Each dependentProperty As EdmProperty In navProperty.GetDependentProperties().Where(Function(p) ef.IsNullable(p)) #> <#=code.Escape(dependentProperty)#> = Nothing <# Next End If #> End If <# Else #> If <#=code.Escape(navProperty)#> IsNot Nothing Then <#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#> = Me <# For Each dependentProperty As EdmProperty In navProperty.GetDependentProperties() #> <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#> <# Next #> End If <# End If Else If navProperty.GetDependentProperties().Any() Then #> If <#=code.Escape(navProperty)#> IsNot Nothing Then <# For Each dependentProperty As EdmProperty In navProperty.GetDependentProperties() #> <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#> <# Next If navProperty.GetDependentProperties().Where(Function(p) ef.IsNullable(p)).Any() Then #> ElseIf Not skipKeys Then <# For Each dependentProperty As EdmProperty In navProperty.GetDependentProperties().Where(Function(p) ef.IsNullable(p)) #> <#=code.Escape(dependentProperty)#> = Nothing <# Next End If #> End If <# ElseIf IsForeignKeyOrIdentifyingRelationship(ef, navProperty) Then #> If <#=code.Escape(navProperty)#> IsNot Nothing Then <# For Each fromProperty As EdmProperty In ef.GetPrincipalProperties(navProperty) #> <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#> = <#=code.Escape(fromProperty)#> <# Next #> End If <# End If End If #> If ChangeTracker.ChangeTrackingEnabled Then If ChangeTracker.OriginalValues.ContainsKey("<#=navProperty.Name#>") AndAlso ChangeTracker.OriginalValues("<#=navProperty.Name#>") Is <#=code.Escape(navProperty)#> Then ChangeTracker.OriginalValues.Remove("<#=navProperty.Name#>") Else ChangeTracker.RecordOriginalValue("<#=navProperty.Name#>", previousValue) <# If ef.IsPrincipalEndOfIdentifyingRelationship(DirectCast(navProperty.FromEndMember, AssociationEndMember)) Then #> ' Delete the dependent end of this identifying association. If the current state is Added, ' allow the relationship to be changed without causing the dependent to be deleted. If previousValue IsNot Nothing AndAlso previousValue.ChangeTracker.State <> ObjectState.Added Then previousValue.MarkAsDeleted() End If <# ElseIf inverse Is Nothing AndAlso ef.IsPrincipalEndOfIdentifyingRelationship(DirectCast(navProperty.ToEndMember, AssociationEndMember)) Then #> ' This is the dependent end of an identifying association, so it must be deleted when the relationship is ' removed. If the current state is Added, the relationship can be changed without causing the dependent to be deleted. ' This is a unidirectional relationship from the dependent to the principal, so the dependent end is ' responsible for cascading the delete. In all other cases the principal end will manage it. If previousValue IsNot Nothing AndAlso ChangeTracker.State <> ObjectState.Added Then Me.MarkAsDeleted() End If <# End If #> End If If <#=code.Escape(navProperty)#> IsNot Nothing AndAlso Not <#=code.Escape(navProperty)#>.ChangeTracker.ChangeTrackingEnabled Then <#=code.Escape(navProperty)#>.StartTracking() End If <# If IsSaveReference(ef, navProperty) Then #> Fixup<#=navProperty.Name#>Keys() <# End If If inverse Is Nothing AndAlso Not IsForeignKeyOrIdentifyingRelationship(ef, navProperty) AndAlso navProperty.FromEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.Many AndAlso navProperty.ToEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.One Then #> If previousValue IsNot Nothing Then previousValue.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(Nothing, False) End If If <#=code.Escape(navProperty)#> IsNot Nothing Then <#=code.Escape(navProperty)#>.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(Me, False) End If <# End If #> End If End Sub <# If IsSaveReference(ef, navProperty) Then Dim targetType As EntityType = DirectCast(navProperty.TypeUsage.EdmType, EntityType) Dim keyNames As List(Of String) = targetType.KeyMembers.Select(Function(x) x.Name).ToList() #> Private Sub Fixup<#=navProperty.Name#>Keys() <# For k As Integer = 0 To keyNames.Count - 1 #> Const <#=CreateKeyNameVariable(code.Escape(keyNames(k)))#> As String = "<#=CreateReferenceValueLookupKey(navProperty, keyNames(k))#>" <# Next #> If ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames(0)))#>)<#=If(keyNames.Count > 1, " AndAlso _", " Then")#> <# For k As Integer = 1 To keyNames.Count - 1 #> ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>)<#=If(k < keyNames.Count - 1, " AndAlso _", " Then")#> <# Next #> If <#=code.Escape(navProperty)#> Is Nothing OrElse <# For k As Integer = 0 To keyNames.Count - 1 Dim equality As String = If(DirectCast(targetType.KeyMembers(keyNames(k)).TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary, "EqualityComparer.Binary", String.Empty) #> Not <#=equality#>Equals(ChangeTracker.ExtendedProperties(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>), <#=code.Escape(navProperty)#>.<#=code.Escape(keyNames(k))#>)<#=If(k < keyNames.Count - 1, " OrElse _", " Then")#> <# Next For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.RecordOriginalValue(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>, ChangeTracker.ExtendedProperties(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>)) <# Next #> End If <# For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.ExtendedProperties.Remove(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>) <# Next #> End If End Sub <# End If End If Next For Each navProperty As NavigationProperty In entity.NavigationProperties.Where(Function(np) np.DeclaringType Is entity) Dim inverse As NavigationProperty = ef.Inverse(navProperty) If inverse IsNot Nothing AndAlso Not IsReadWriteAccessibleProperty(inverse) Then inverse = Nothing End If If navProperty.ToEndMember.RelationshipMultiplicity = RelationshipMultiplicity.Many Then #> Private Sub Fixup<#=navProperty.Name#>(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs) If IsDeserializing Then Return End If If e.NewItems IsNot Nothing Then For Each item As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> In e.NewItems <# If inverse IsNot Nothing Then If inverse.ToEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.Many Then #> item.<#=code.Escape(inverse)#> = Me <# Else #> If Not item.<#=code.Escape(inverse)#>.Contains(Me) Then item.<#=code.Escape(inverse)#>.Add(Me) End If <# End If ElseIf IsForeignKeyOrIdentifyingRelationship(ef, navProperty) Then For Each fromProperty As EdmProperty In ef.GetPrincipalProperties(navProperty) #> item.<#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#> = <#=code.Escape(fromProperty)#> <# Next ElseIf navProperty.FromEndMember.RelationshipMultiplicity = RelationshipMultiplicity.ZeroOrOne Then #> item.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(Me, False) <# End If #> If ChangeTracker.ChangeTrackingEnabled Then If Not item.ChangeTracker.ChangeTrackingEnabled Then item.StartTracking() End If ChangeTracker.RecordAdditionToCollectionProperties("<#=code.Escape(navProperty)#>", item) End If <# If ef.IsCascadeDeletePrincipal(navProperty) Then #> ' This is the principal end in an association that performs cascade deletes. ' Update the event listener to refer to the new dependent. AddHandler ChangeTracker.ObjectStateChanging, AddressOf item.HandleCascadeDelete <# End If #> Next End If If e.OldItems IsNot Nothing Then For Each item As <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> In e.OldItems <# If inverse IsNot Nothing Then If inverse.ToEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.Many Then #> If ReferenceEquals(item.<#=code.Escape(inverse)#>, Me) Then item.<#=code.Escape(inverse)#> = Nothing End If <# Else #> If item.<#=code.Escape(inverse)#>.Contains(Me) Then item.<#=code.Escape(inverse)#>.Remove(Me) End If <# End If ElseIf IsForeignKeyOrIdentifyingRelationship(ef, navProperty) Then For Each fromProperty As EdmProperty In ef.GetPrincipalProperties(navProperty) Dim p As EdmProperty = ef.GetCorrespondingDependentProperty(navProperty, fromProperty) If ef.IsNullable(p.TypeUsage) Then #> item.<#=code.Escape(p)#> = Nothing <# End If Next ElseIf navProperty.FromEndMember.RelationshipMultiplicity = RelationshipMultiplicity.ZeroOrOne Then #> item.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(Nothing, False) <# End If #> If ChangeTracker.ChangeTrackingEnabled Then ChangeTracker.RecordRemovalFromCollectionProperties("<#=code.Escape(navProperty)#>", item) <# If ef.IsPrincipalEndOfIdentifyingRelationship(DirectCast(navProperty.FromEndMember, AssociationEndMember)) Then #> ' Delete the dependent end of this identifying association. If the current state is Added, ' allow the relationship to be changed without causing the dependent to be deleted. If item.ChangeTracker.State <> ObjectState.Added Then item.MarkAsDeleted() End If <# End If #> End If <# If ef.IsCascadeDeletePrincipal(navProperty) Then #> ' This is the principal end in an association that performs cascade deletes. ' Remove the previous dependent from the event listener. RemoveHandler ChangeTracker.ObjectStateChanging, AddressOf item.HandleCascadeDelete <# End If #> Next End If End Sub <# End If Next For Each associationEnd As AssociationEndMember In shadowAssociationEnds Dim association As AssociationType = TryCast(associationEnd.DeclaringType, AssociationType) Dim targetType As EntityType = TryCast(DirectCast(associationEnd.TypeUsage.EdmType, RefType).ElementType, EntityType) Dim keyNames As List(Of String) = targetType.KeyMembers.Select(Function(x) x.Name).ToList() #> Friend Sub <#=CreateFixupMethodName(associationEnd)#>(ByVal value As <#=code.Escape(targetType)#>, ByVal forceRemove As Boolean) <# For k As Integer = 0 To keyNames.Count - 1 #> Const <#=CreateKeyNameVariable(code.Escape(keyNames(k)))#> As String = "<#=CreateReferenceValueLookupKey(associationEnd, keyNames(k))#>" <# Next #> If ChangeTracker.ChangeTrackingEnabled AndAlso <# For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>)<#=If(k < (keyNames.Count - 1), " AndAlso _", " Then")#> <# Next #> If forceRemove OrElse <# For k As Integer = 0 To keyNames.Count - 1 Dim equality As String = If(DirectCast(targetType.KeyMembers(keyNames(k)).TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary, "EqualityComparer.Binary", String.Empty) #> Not <#=equality#>Equals(ChangeTracker.ExtendedProperties(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>), If(value Is Nothing, Nothing, DirectCast(value.<#=code.Escape(keyNames(k))#>, Object)))<#=If(k < keyNames.Count - 1, " OrElse _", " Then")#> <# Next For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.RecordOriginalValue(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>, ChangeTracker.ExtendedProperties(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>)) <# Next #> If value Is Nothing Then <# For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.ExtendedProperties.Remove(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>) <# Next #> Else <# For k As Integer = 0 To keyNames.Count - 1 #> ChangeTracker.ExtendedProperties(<#=CreateKeyNameVariable(code.Escape(keyNames(k)))#>) = value.<#=code.Escape(keyNames(k))#> <# Next #> End If End If End If End Sub <# Next region.End() #> End Class <# EndNamespace(namespaceName) Next ' Emit Complex Types For Each loopComplex As ComplexType In ItemCollection.GetItems(Of ComplexType)().OrderBy(Function(e) e.Name) Dim complex As ComplexType = loopComplex fileManager.StartNewFile(complex.Name & ".vb") BeginNamespace(namespaceName, code) #> Partial <#=Accessibility.ForType(complex)#> Class <#=code.Escape(complex)#> Implements INotifyComplexPropertyChanging Implements INotifyPropertyChanged <# region.Begin("Primitive Properties") For Each edmProperty As EdmProperty In complex.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is PrimitiveType AndAlso p.DeclaringType Is complex) #> <#=Accessibility.ForProperty(edmProperty)#> Property <#=code.Escape(edmProperty)#>() As <#=code.Escape(edmProperty.TypeUsage)#> Get Return <#=code.FieldName(edmProperty)#> End Get Set(ByVal value As <#=code.Escape(edmProperty.TypeUsage)#>) <# If DirectCast(edmProperty.TypeUsage.EdmType, PrimitiveType).PrimitiveTypeKind = PrimitiveTypeKind.Binary Then #> If <#=code.FieldName(edmProperty)#> IsNot value Then <# Else #> If Not Equals(<#=code.FieldName(edmProperty)#>, value) Then <# End If #> OnComplexPropertyChanging() <#=code.FieldName(edmProperty)#> = value OnPropertyChanged("<#=edmProperty.Name#>") End If End Set End Property Private <#=code.FieldName(edmProperty)#> As <#=code.Escape(edmProperty.TypeUsage)#> <# Next region.End() region.Begin("Complex Properties") For Each edmProperty As EdmProperty In complex.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is ComplexType AndAlso p.DeclaringType Is complex) #> <#=Accessibility.ForProperty(edmProperty)#> Property <#=code.Escape(edmProperty)#>() As <#=code.Escape(edmProperty.TypeUsage)#> <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>Get If Not <#=InitializedTrackingField(edmProperty, code)#> AndAlso <#=code.FieldName(edmProperty)#> Is Nothing Then <#=code.FieldName(edmProperty)#> = New <#=code.Escape(edmProperty.TypeUsage)#>() AddHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf HandleComplexPropertyChanging End If <#=InitializedTrackingField(edmProperty, code)#> = True Return <#=code.FieldName(edmProperty)#> End Get Set(ByVal value As <#=code.Escape(edmProperty.TypeUsage)#>) <#=InitializedTrackingField(edmProperty, code)#> = True If Not Equals(<#=code.FieldName(edmProperty)#>, value) Then If <#=code.FieldName(edmProperty)#> IsNot Nothing Then RemoveHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf HandleComplexPropertyChanging End If OnComplexPropertyChanging() <#=code.FieldName(edmProperty)#> = value OnPropertyChanged("<#=edmProperty.Name#>") If value IsNot Nothing Then AddHandler DirectCast(<#=code.FieldName(edmProperty)#>, INotifyComplexPropertyChanging).ComplexPropertyChanging, AddressOf HandleComplexPropertyChanging End If End If End Set End Property Private <#=code.FieldName(edmProperty)#> As <#=code.Escape(edmProperty.TypeUsage)#> Private <#=InitializedTrackingField(edmProperty, code)#> As Boolean <# Next region.End() region.Begin("ChangeTracking") #> Private Sub OnComplexPropertyChanging() RaiseEvent ComplexPropertyChanging(Me, New EventArgs()) End Sub Private Event ComplexPropertyChanging As EventHandler Implements INotifyComplexPropertyChanging.ComplexPropertyChanging Private Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Private Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged <# If complex.Properties.Where(Function(p) TypeOf p.TypeUsage.EdmType Is ComplexType AndAlso p.DeclaringType Is complex).Count() > 0 Then #> Private Sub HandleComplexPropertyChanging(ByVal sender As Object, ByVal args As EventArgs) ' Bubble the event to all listeners because something changed in a nested complex property OnComplexPropertyChanging() End Sub <# End If #> Public Shared Sub RecordComplexOriginalValues(ByVal parentPropertyName As String, ByVal complexObject As <#=code.Escape(complex)#>, ByVal changeTracker As ObjectChangeTracker) If String.IsNullOrEmpty(parentPropertyName) Then Throw New ArgumentException("String parameter cannot be null or empty.", "parentPropertyName") End If If changeTracker Is Nothing Then Throw New ArgumentNullException("changeTracker") End If <# For Each complexProperty As EdmProperty in complex.Properties If TypeOf complexProperty.TypeUsage.EdmType Is ComplexType Then #> <#=code.Escape(complexProperty.TypeUsage)#>.RecordComplexOriginalValues(String.Format(CultureInfo.InvariantCulture, "{0}.<#=complexProperty.Name#>", parentPropertyName), If(complexObject Is Nothing, Nothing, complexObject.<#=code.Escape(complexProperty)#>), changeTracker) <# Else #> changeTracker.RecordOriginalValue(String.Format(CultureInfo.InvariantCulture, "{0}.<#=complexProperty.Name#>", parentPropertyName), If(complexObject Is Nothing, Nothing, complexObject.<#=code.Escape(complexProperty)#>)) <# End If Next #> End Sub <# region.End() #> End Class <# EndNamespace(namespaceName) Next If Not VerifyTypesAreCaseInsensitiveUnique(ItemCollection) Then Return "" End If fileManager.Process() #> <#+ Private Sub WriteHeader(ByVal fileManager As EntityFrameworkTemplateFileManager, ByVal ParamArray extraUsings As String()) fileManager.StartHeader() #> '------------------------------------------------------------------------------ ' ' This code was generated from a template. ' ' Changes to this file may cause incorrect behavior and will be lost if ' the code is regenerated. ' '------------------------------------------------------------------------------ Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Collections.Specialized Imports System.ComponentModel Imports System.Globalization Imports System.Runtime.Serialization Imports System.Runtime.CompilerServices <#=String.Join(String.Empty, extraUsings.Select(Function(u) "Imports " & u & Environment.NewLine).ToArray())#> <#+ fileManager.EndBlock() End Sub Private Sub BeginNamespace(ByVal namespaceName As String, code As CodeGenerationTools) Dim region As CodeRegion = New CodeRegion(Me) If Not String.IsNullOrEmpty(namespaceName) Then #> Namespace <#=code.EscapeNamespace(namespaceName)#> <#+ PushIndent(CodeRegion.GetIndent(1)) End If End Sub Private Sub EndNamespace(ByVal namespaceName As String) If Not String.IsNullOrEmpty(namespaceName) Then PopIndent() #> End Namespace <#+ End If End Sub Private Function IsReadWriteAccessibleProperty(ByVal member As EdmMember) As Boolean Dim setter As String = Accessibility.ForWriteOnlyProperty(member) Dim getter As String = Accessibility.ForReadOnlyProperty(member) Return getter <> "Private" AndAlso getter <> "Protected" AndAlso setter <> "Private" AndAlso setter <> "Protected" End Function Private Function InitializedTrackingField(ByVal edmProperty As EdmProperty, code As CodeGenerationTools) As String Dim namePart As String = edmProperty.Name & "Initialized" If code.CamelCaseFields Then namePart = code.CamelCase(namePart) End If Return "_" & namePart End Function Private Sub WriteEntityTypeSerializationInfo(ByVal type As EntityType, ByVal itemCollection As ItemCollection, ByVal code As CodeGenerationTools, ByVal tools As MetadataTools) #> <#+ For Each subType As EntityType In tools.GetSubtypesOf(type, itemCollection, True) #> ))> <#+ Next Dim knownNavPropertyTypes As List(Of EntityType) = New List(Of EntityType)() For Each navProperty As NavigationProperty In type.NavigationProperties.Where(Function(np) np.DeclaringType Is type) Dim navPropertyType As EntityType = navProperty.ToEndMember.GetEntityType() If Not knownNavPropertyTypes.Contains(navPropertyType) Then knownNavPropertyTypes.Add(navPropertyType) End If Next For Each knownNavPropertyType As EntityType in knownNavPropertyTypes #> ))> <#+ Next End Sub Private Sub WriteCustomObservableCollection() #> ' An System.Collections.ObjectModel.ObservableCollection that raises ' individual item removal notifications on clear and prevents adding duplicates. Public Class TrackableCollection(Of T) Inherits ObservableCollection(Of T) Protected Overrides Sub ClearItems() Dim items As New List(Of T)(Me) items.ForEach(Function(t) Remove(t)) End Sub Protected Overloads Overrides Sub InsertItem(ByVal index As Integer, ByVal item As T) If Not Me.Contains(item) Then MyBase.InsertItem(index, item) End If End Sub End Class <#+ End Sub Private Function IsSaveReference(ByVal tools as MetadataTools, ByVal navProperty As NavigationProperty) As Boolean ' Target is a reference, Source is nullable (i.e. not a PK) Return Not IsForeignKeyOrIdentifyingRelationship(tools, navProperty) AndAlso navProperty.ToEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.Many AndAlso navProperty.FromEndMember.RelationshipMultiplicity <> RelationshipMultiplicity.One End Function Private Function CreateFixupMethodName(ByVal endMember As RelationshipEndMember) As String Return String.Format(CultureInfo.InvariantCulture, "Fixup{0}_{1}_{2}Keys", endMember.DeclaringType.NamespaceName, endMember.DeclaringType.Name, endMember.Name) End Function Private Function CreateKeyNameVariable(ByVal keyName As String) As String Return String.Format(CultureInfo.InvariantCulture, "{0}KeyName", keyName) End Function Private Function CreateReferenceValueLookupKey(ByVal endMember As AssociationEndMember, ByVal keyName As String) As String Return String.Format(CultureInfo.InvariantCulture, "Navigate({0}.{1}).{2}", endMember.DeclaringType.FullName, endMember.Name, keyName) End Function Private Function CreateReferenceValueLookupKey(ByVal navProp As NavigationProperty, ByVal keyName As String) As String Return String.Format(CultureInfo.InvariantCulture, "{0}.{1}", navProp.Name, keyName) End Function Private Sub WriteObjectChangeTracker() #> ' Helper class that captures most of the change tracking work that needs to be done ' for self tracking entities. Public Class ObjectChangeTracker #Region "Fields" Private _isDeserializing As Boolean Private _objectState As ObjectState = ObjectState.Added Private _originalValues As OriginalValuesDictionary Private _extendedProperties As ExtendedPropertiesDictionary Private _changeTrackingEnabled As Boolean Private _objectsAddedToCollections As New ObjectsAddedToCollectionProperties() Private _objectsRemovedFromCollections As New ObjectsRemovedFromCollectionProperties() #End Region #Region "Events" Public Event ObjectStateChanging As EventHandler(Of ObjectStateChangingEventArgs) #End Region Protected Overridable Sub OnObjectStateChanging(ByVal newState As ObjectState) RaiseEvent ObjectStateChanging(Me, New ObjectStateChangingEventArgs With {.NewState = newState}) End Sub Public Property State() As ObjectState Get Return _objectState End Get Set(ByVal value As ObjectState) If _isDeserializing Or _changeTrackingEnabled Then OnObjectStateChanging(value) _objectState = value End If End Set End Property Public Property ChangeTrackingEnabled() As Boolean Get Return _changeTrackingEnabled End Get Set(ByVal value As Boolean) _changeTrackingEnabled = value End Set End Property ' Returns the removed objects to collection valued properties that were changed. Public ReadOnly Property ObjectsRemovedFromCollectionProperties() As ObjectsRemovedFromCollectionProperties Get If _objectsRemovedFromCollections Is Nothing Then _objectsRemovedFromCollections = New ObjectsRemovedFromCollectionProperties() End If Return _objectsRemovedFromCollections End Get End Property ' Returns the original values for properties that were changed. Public ReadOnly Property OriginalValues() As OriginalValuesDictionary Get If _originalValues Is Nothing Then _originalValues = New OriginalValuesDictionary() End If Return _originalValues End Get End Property ' Returns the extended property values. ' This includes key values for independent associations that are needed for the ' concurrency model in the Entity Framework Public ReadOnly Property ExtendedProperties() As ExtendedPropertiesDictionary Get If _extendedProperties Is Nothing Then _extendedProperties = New ExtendedPropertiesDictionary() End If Return _extendedProperties End Get End Property ' Returns the added objects to collection valued properties that were changed. Public ReadOnly Property ObjectsAddedToCollectionProperties() As ObjectsAddedToCollectionProperties Get If _objectsAddedToCollections Is Nothing Then _objectsAddedToCollections = New ObjectsAddedToCollectionProperties() End If Return _objectsAddedToCollections End Get End Property #Region "MethodsForChangeTrackingOnClient" Public Sub OnDeserializingMethod(ByVal context As StreamingContext) _isDeserializing = True End Sub Public Sub OnDeserializedMethod(ByVal context As StreamingContext) _isDeserializing = False End Sub ' Resets the ObjectChangeTracker to the Unchanged state and ' clears the original values as well as the record of changes ' to collection properties Public Sub AcceptChanges() OnObjectStateChanging(ObjectState.Unchanged) OriginalValues.Clear() ObjectsAddedToCollectionProperties.Clear() ObjectsRemovedFromCollectionProperties.Clear() ChangeTrackingEnabled = True _objectState = ObjectState.Unchanged End Sub ' Captures the original value for a property that is changing. Friend Sub RecordOriginalValue(ByVal propertyName As String, ByVal value As Object) If _changeTrackingEnabled AndAlso _objectState <> ObjectState.Added Then If Not Me.OriginalValues.ContainsKey(propertyName) Then OriginalValues(propertyName) = value End If End If End Sub ' Records an addition to collection valued properties on SelfTracking Entities. Friend Sub RecordAdditionToCollectionProperties(ByVal propertyName As String, ByVal value As Object) If _changeTrackingEnabled Then ' Add the entity back after deleting it, we should do nothing here then If Me.ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName) AndAlso Me.ObjectsRemovedFromCollectionProperties(propertyName).Contains(value) Then Me.ObjectsRemovedFromCollectionProperties(propertyName).Remove(value) If Me.ObjectsRemovedFromCollectionProperties(propertyName).Count = 0 Then Me.ObjectsRemovedFromCollectionProperties.Remove(propertyName) End If Exit Sub End If If Not Me.ObjectsAddedToCollectionProperties.ContainsKey(propertyName) Then ObjectsAddedToCollectionProperties(propertyName) = New ObjectList() ObjectsAddedToCollectionProperties(propertyName).Add(value) Else ObjectsAddedToCollectionProperties(propertyName).Add(value) End If End If End Sub ' Records a removal to collection valued properties on SelfTracking Entities. Friend Sub RecordRemovalFromCollectionProperties(ByVal propertyName As String, ByVal value As Object) If _changeTrackingEnabled Then ' Delete the entity back after adding it, we should do nothing here then If Me.ObjectsAddedToCollectionProperties.ContainsKey(propertyName) AndAlso Me.ObjectsAddedToCollectionProperties(propertyName).Contains(value) Then Me.ObjectsAddedToCollectionProperties(propertyName).Remove(value) If Me.ObjectsAddedToCollectionProperties(propertyName).Count = 0 Then Me.ObjectsAddedToCollectionProperties.Remove(propertyName) End If Exit Sub End If If Not Me.ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName) Then ObjectsRemovedFromCollectionProperties(propertyName) = New ObjectList() ObjectsRemovedFromCollectionProperties(propertyName).Add(value) Else If Not ObjectsRemovedFromCollectionProperties(propertyName).Contains(value) Then ObjectsRemovedFromCollectionProperties(propertyName).Add(value) End If End If End If End Sub #End Region End Class #Region "EnumForObjectState" Public Enum ObjectState Unchanged = &H1 Added = &H2 Modified = &H4 Deleted = &H8 End Enum #End Region Public Class ObjectsAddedToCollectionProperties Inherits Dictionary(Of String, ObjectList) End Class Public Class ObjectsRemovedFromCollectionProperties Inherits Dictionary(Of String, ObjectList) End Class Public Class OriginalValuesDictionary Inherits Dictionary(Of String, Object) End Class Public Class ExtendedPropertiesDictionary Inherits Dictionary(Of String, Object) End Class Public Class ObjectList Inherits List(Of Object) End Class <#+ End Sub Private Sub WriteINotifyComplexPropertyChanging() #> ' An interface that provides an event that fires when complex properties change. ' Changes can be the replacement of a complex property with a new complex type instance or ' a change to a scalar property within a complex type instance. Public Interface INotifyComplexPropertyChanging Event ComplexPropertyChanging As EventHandler End Interface <#+ End Sub Private Sub WriteIObjectWithChangeTracker() #> ' The interface is implemented by the self tracking entities that EF will generate. ' We will have an Adapter that converts this interface to the interface that the EF expects. ' The Adapter will live on the server side. Public Interface IObjectWithChangeTracker ' Has all the change tracking information for the subgraph of a given object. Property ChangeTracker() As ObjectChangeTracker End Interface Public Class ObjectStateChangingEventArgs Inherits EventArgs Private _NewState As ObjectState Public Property NewState() As ObjectState Get Return _NewState End Get Set(ByVal value As ObjectState) _NewState = value End Set End Property End Class Public Module ObjectWithChangeTrackerExtensions Public Function MarkAsDeleted(Of T As IObjectWithChangeTracker)(ByVal trackingItem As T) As T If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = True trackingItem.ChangeTracker.State = ObjectState.Deleted Return trackingItem End Function Public Function MarkAsAdded(Of T As IObjectWithChangeTracker)(ByVal trackingItem As T) As T If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = True trackingItem.ChangeTracker.State = ObjectState.Added Return trackingItem End Function Public Function MarkAsModified(Of T As IObjectWithChangeTracker)(ByVal trackingItem As T) As T If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = True trackingItem.ChangeTracker.State = ObjectState.Modified Return trackingItem End Function Public Function MarkAsUnchanged(Of T As IObjectWithChangeTracker)(ByVal trackingItem As T) As T If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = True trackingItem.ChangeTracker.State = ObjectState.Unchanged Return trackingItem End Function Public Sub StartTracking(ByVal trackingItem As IObjectWithChangeTracker) If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = True End Sub Public Sub StopTracking(ByVal trackingItem As IObjectWithChangeTracker) If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.ChangeTrackingEnabled = False End Sub Public Sub AcceptChanges(ByVal trackingItem As IObjectWithChangeTracker) If trackingItem Is Nothing Then Throw New ArgumentNullException("trackingItem") End If trackingItem.ChangeTracker.AcceptChanges() End Sub End Module <#+ End Sub Private Sub WriteEqualityComparer #> Public Module EqualityComparer ' Helper method to determine if two byte arrays are the same value even if they are different object references Public Function BinaryEquals(ByVal binaryValue1 As Object, ByVal binaryValue2 As Object) As Boolean If Object.ReferenceEquals(binaryValue1, binaryValue2) Then Return True End If Dim array1 As Byte() = TryCast(binaryValue1, Byte()) Dim array2 As Byte() = TryCast(binaryValue2, Byte()) If array1 IsNot Nothing AndAlso array2 IsNot Nothing Then If array1.Length <> array2.Length Then Return False End If For i As Integer = 0 To array1.Length - 1 If array1(i) <> array2(i) Then Return False End If Next Return True End If Return False End Function End Module <#+ End Sub Private Function InheritanceOperator(ByVal entity As StructuralType) As String If entity.BaseType Is Nothing Then Return "Overridable " Else Return "Overloads Overrides " End If End Function Private Function VerifyTypesAreCaseInsensitiveUnique(ByVal itemCollection As EdmItemCollection) As Boolean Dim alreadySeen As New Dictionary(Of String, Boolean)(StringComparer.OrdinalIgnoreCase) For Each type As StructuralType In itemCollection.GetItems(Of StructuralType)() If Not (TypeOf type Is EntityType OrElse TypeOf type Is ComplexType) Then Continue For End If If alreadySeen.ContainsKey(type.FullName) Then [Error](String.Format(CultureInfo.CurrentCulture, "This template does not support types that differ only by case, the types {0} are not supported", type.FullName)) Return False Else alreadySeen.Add(type.FullName, True) End If Next Return True End Function ' True if the association for the specified navigation property is an identifying relationship or a foreign key relationship. Private Function IsForeignKeyOrIdentifyingRelationship(ByVal tools As MetadataTools, ByVal navProperty As NavigationProperty) As Boolean If tools Is Nothing Then Throw New ArgumentNullException("tools") End If If navProperty Is Nothing Then Throw New ArgumentNullException("navProperty") End If Return IsForeignKeyOrIdentifyingRelationship(tools, DirectCast(navProperty.RelationshipType, AssociationType)) End Function ' True if the specified association is an identifying relationship or a foreign key relationship. Private Function IsForeignKeyOrIdentifyingRelationship(ByVal tools As MetadataTools, ByVal association As AssociationType) As Boolean If tools Is Nothing Then Throw New ArgumentNullException("tools") End If If association Is Nothing Then Throw New ArgumentNullException("association") End If Return association.IsForeignKey OrElse tools.IsIdentifyingRelationship(association) End Function ' Set recordRequiredOriginalValuesOnly to false in the OriginalValueMembers constructor in order to always record all original values Public Class OriginalValueMembers Private ReadOnly _concurrencyMembers As HashSet(Of EdmProperty) Public Sub New(ByVal recordRequiredOriginalValuesOnly As Boolean, ByVal metadataWorkspace As MetadataWorkspace, ByVal metadataTools As MetadataTools) If recordRequiredOriginalValuesOnly Then Try _concurrencyMembers = New HashSet(Of EdmProperty)() For Each container As EntityContainer In metadataWorkspace.GetItems(Of EntityContainer)(DataSpace.CSpace) Dim directSubTypeLookup As ILookup(Of EntityType, EntityType) = metadataWorkspace.GetItems(Of EntityType)(DataSpace.CSpace).ToLookup(Function(e) DirectCast(e.BaseType, EntityType)) For Each eSet As EntitySetBase In container.BaseEntitySets.Where(Function(es) es.BuiltInTypeKind = BuiltInTypeKind.EntitySet) Dim subTypes As New List(Of EntityType)() GetSubtypes(directSubTypeLookup, DirectCast(eSet.ElementType, EntityType), subTypes) For Each eType As EntityType In subTypes For Each member As EdmProperty In metadataWorkspace.GetRequiredOriginalValueMembers(eSet, eType) _concurrencyMembers.Add(member) Next Next Next Next ' GetRequiredOriginalValueMembers will not always return foreign key properties, but they are required For Each assoc As AssociationType In metadataWorkspace.GetItems(Of AssociationType)(DataSpace.CSpace).Where(Function(a) a.IsForeignKey) For Each toProperty As EdmProperty In assoc.ReferentialConstraints(0).ToProperties _concurrencyMembers.Add(toProperty) Next Next Catch ex As Exception ' If any exceptions occur, fall back to always recording original values for all properties _concurrencyMembers = Nothing End Try End If End Sub Public Function IsOriginalValueMember(ByVal edmProperty As EdmProperty) As Boolean Return _concurrencyMembers Is Nothing OrElse _concurrencyMembers.Contains(edmProperty) End Function Private Shared Sub GetSubtypes(ByVal lookup As ILookup(Of EntityType, EntityType), ByVal eType As EntityType, ByVal subTypes As List(Of EntityType)) subTypes.Add(eType) For Each subType As EntityType In lookup(eType) GetSubtypes(lookup, subType, subTypes) Next End Sub End Class #>